﻿using System;
using System.Threading;

namespace Restful.Net
{
    public class AsyncResult : IAsyncResult
    {
        private readonly AsyncCallback asyncCallback;
        private long completed;
        private ManualResetEvent waitHandle;

        private IAsyncResult innerAsyncResult;
        
        public AsyncResult(AsyncCallback asyncCallback, object state)
        {
            this.asyncCallback = asyncCallback;
            AsyncState = state;
        }

        public IAsyncResult InnerAsyncResult
        {
            get
            {
                return innerAsyncResult;
            }
        }

        public ServiceState ServiceState
        {
            get;
            set;
        }

        public bool IsCompleted
        {
            get
            {
                return Interlocked.Read(ref completed) != 0;
            }
        }

        public WaitHandle AsyncWaitHandle
        {
            get
            {
                if (waitHandle == null)
                {
                    ManualResetEvent evt = new ManualResetEvent(false);
                    if (Interlocked.CompareExchange(ref waitHandle, evt, null) != null)
                    {
                        evt.Close();
                    }
                }

                return waitHandle;
            }
        }

        public virtual void StartSynchronously(Action action)
        {
            Exception e = null;
            try
            {
                RestContext.Current = ServiceState.Context;
                action();
            }
            catch (Exception ee)
            {
                e = ee;
            }
            finally
            {
                SetAsCompletedSynchronously(e);
            }
        }

        public void EndSynchronously()
        {
            if (Exception != null)
            {
                throw Exception;
            }
        }

        public IAsyncResult BeginInvoke(Func<AsyncCallback, object, IAsyncResult> beginMethod)
        {
            ThreadPool.QueueUserWorkItem(cb =>
            {
                RestContext.Current = ServiceState.Context;
                innerAsyncResult = beginMethod.Invoke(OnComplete, null);
            });

            return this;
        }

        protected void SetAsCompletedSynchronously(Exception e)
        {
            Exception = e;
            Interlocked.Exchange(ref completed, 2);
        }


        private void OnComplete(IAsyncResult result)
        {
            asyncCallback(this);
        }
      
        public Exception Exception
        {
            get;
            private set;
        }

        public object AsyncState
        {
            get;
            private set;
        }

        public bool CompletedSynchronously
        {
            get
            {
                return Interlocked.Read(ref completed) == 2;
            }
        }
    }
}
